package cn.tedu.mall.seckill.timer.job;

import cn.tedu.mall.pojo.seckill.model.SeckillSku;
import cn.tedu.mall.pojo.seckill.model.SeckillSpu;
import cn.tedu.mall.seckill.mapper.SeckillSkuMapper;
import cn.tedu.mall.seckill.mapper.SeckillSpuMapper;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.RandomUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author BJ
 * @date 2022/11/8 19:29
 */
@Slf4j
public class SeckillInitJob implements Job {
    @Autowired
    private SeckillSkuMapper skuMapper;
    @Autowired
    private SeckillSpuMapper spuMapper;

    // 会将对象序列化成二进制文件保存在内存中，不能对数据进行修改
    @Autowired
    private RedisTemplate redisTemplate;
    // 将对象以字符串的形式保存在内存中，可以将秒杀受苦商品的库存数量放在Redis中进行计算
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 【缓存预热方法】
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 【在商品开始秒杀的前五分钟进行商品缓存预热】
        // 1.根据时间范围查出秒杀的spu商品信息
        LocalDateTime nowTime = LocalDateTime.now().plusMinutes(5);
        List<SeckillSpu> seckillSpus = spuMapper.findSeckillSpuByTime(nowTime);
        for (SeckillSpu seckillSpu : seckillSpus) {
            // 2.根据spuId查询要秒杀的sku信息
            List<SeckillSku> seckillSkus = skuMapper.findSeckillSkusBySpuId(seckillSpu.getSpuId());
            for (SeckillSku seckillSku : seckillSkus) {
                // 3.得到要秒杀的sku商品的库存数，然后存入到Redis中
                // 3.1 为要存入的sku商品信息的值创建对应的key
                String stockKey = SeckillCacheUtils.getStockKey(seckillSku.getSkuId());
                if (redisTemplate.hasKey(stockKey)) {
                    log.info("{}号sku商品已经缓存过了",seckillSku.getSkuId());
                }else {
                    // 不存在当前商品的key，就将这个key和对应sku商品的库存数存入
                    // 设置过期时间+随机数，防止缓存雪崩
                    stringRedisTemplate.boundValueOps(stockKey).set(seckillSku.getSeckillStock().toString(),
                                    125*60*1000+ RandomUtils.nextInt(10000), TimeUnit.MILLISECONDS);
                    log.info("{}号sku商品已经存入Redis中",seckillSku.getSkuId());
                }
            }
            // 4.开始预热spu商品对应的随机码，随机码是用户提交订单的依据，没有随机码下单失败
            // 4.1 创建key
            String randCodeKey = SeckillCacheUtils.getRandCodeKey(seckillSpu.getSpuId());
            if (redisTemplate.hasKey(randCodeKey)){
                // 如果当前随机码存在，为了之后测试，将正确的随机码输出到控制台
                int randCode = (int) redisTemplate.boundValueOps(randCodeKey).get();
                log.info("随机码已经存在，当前随机码为：{}",randCode);
            }else {
                // 如果随机码不存在，就生成随机码。保存在Redis中 (100000~999999)
                int randCode = RandomUtils.nextInt(900000)+100000;
                redisTemplate.boundValueOps(randCodeKey).set(randCode,
                        125*60*1000+RandomUtils.nextInt(10000),TimeUnit.MILLISECONDS);
            }

        }
    }
}
